package cn.cloud9.designpattern.dp05proxy.proxy_03_jdk_dynamic.proxy_pack;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Arrays;

/**
 * @projectName: Java
 * @author: Cloud9
 * @date: 2022年05月23日 16:20
 * @version: 1.0
 */
public class MyProxy {

    private static final String NEW_ROW = "\r\n";

    public static Object newProxyInstance(
        MyClassLoader loader,
        Class<?>[] interfaces,
        MyInvocationHandler handler
    ) {
        FileWriter fileWriter = null;
        try {
            // 1、动态生成源码资源 代理类
            String source = generateSourceFile(interfaces);
            System.out.println(source);

            // 2、资源写入到磁盘
            String filePath = MyProxy.class.getResource("").getPath();
            final File file = new File(filePath + "$Proxy0.java");
            fileWriter = new FileWriter(file);
            fileWriter.write(source);
            fileWriter.flush();
            fileWriter.close();

            // 3、编译资源 生成Class文件
            final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            final StandardJavaFileManager manager = compiler.getStandardFileManager(
                null,
                null,
                null
            );
            final Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = compiler.getTask(
                null,
                manager,
                null,
                null,
                null,
                iterable
            );
            task.call();
            manager.close();

            // 4、把生成的Class文件 加载到JVM中
            final Class<?> $proxy0Class = loader.findClass("$Proxy0");
            final Constructor<?> constructor = $proxy0Class.getConstructor(MyInvocationHandler.class);
            // 如果不要保留class文件, 调用删除方法
//            file.delete();

            // 5、返回这个代理对象
            return constructor.newInstance(handler);
        } catch (Exception e) {
            e.printStackTrace();
        }


        return null;
    }

    public static String generateSourceFile(Class<?>[] interfaces) {
        Class<?> interfaceClass = interfaces[0];

        StringBuilder builder = new StringBuilder();

        // 定位包位置
        builder
            .append("package ")
            .append(MyProxy.class.getPackage().getName())
            .append(";")
            .append(NEW_ROW);

        // 导入接口
        builder
            .append("import cn.cloud9.designpattern.dp05proxy.proxy_01_static.Tenement;")
            .append(NEW_ROW);

        // 导入反射资源
        builder
            .append("import java.lang.reflect.*;")
            .append(NEW_ROW);

        // 类的声明, 为什么实现的接口只需要取第一个？？？
        builder
            .append("public class $Proxy0 implements ")
            .append(interfaceClass.getName())
            .append("{")
            .append(NEW_ROW);

        // 类成员声明
        builder.append("\tMyInvocationHandler handler;").append(NEW_ROW);

        // 构造器
        builder.append("\tpublic $Proxy0(MyInvocationHandler handler) {").append(NEW_ROW);
        builder.append("\t\tthis.handler = handler;").append(NEW_ROW);
        builder.append("\t}").append(NEW_ROW);

        // 接口的方法
        Arrays.stream(interfaceClass.getMethods()).forEach(method -> {
//            Class<?>[] parameterTypes = method.getParameterTypes();
//            StringBuilder paramNameBuilder = new StringBuilder();
//            StringBuilder paramValueBuilder = new StringBuilder();
//            StringBuilder paramClassBuilder = new StringBuilder();
//
//            Arrays.stream(parameterTypes).forEach(paramClass -> {
//
//            });

            // 方法声明
            builder
                .append("\tpublic ")
                .append(method.getReturnType().getName())
                .append(" ")
                .append(method.getName())
                .append("() {")
                .append(NEW_ROW);

            // 方法体异常捕获
            builder
                .append("\t\ttry {").append(NEW_ROW)
                    // 方法获取
                    .append("\t\t\tMethod method = ").append(interfaces[0].getName())
                    .append(".class.getMethod(\"").append(method.getName()).append("\", new Class[] {});").append(NEW_ROW)

                    // 方法调用
                    .append("\t\t\tthis.handler.invoke(this, method, new Object[] {});").append(NEW_ROW)

                    // 方法返回
                    .append("\t\t\treturn ;").append(NEW_ROW)
                .append("\t\t} catch (Error error) {\n\t\t\t error.printStackTrace();\n\t\t}")
                .append(" catch (Throwable throwable) {\n\t\t\t throw new UndeclaredThrowableException(throwable);\n\t\t}").append(NEW_ROW);

            // 方法结束符
            builder.append("\t}").append(NEW_ROW);
        });


        // 结束符
        builder.append("}").append(NEW_ROW);

        return builder.toString();
    }
}
